﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using AIProject.Utility;

namespace AIProject.Pylos
{
    class State
    {
        private RandomNumbers rn;
        private Piece[,] board;
        private Boolean whiteToMove;
        private Boolean whiteWon;
        private Boolean blackWon;
        private Boolean tie;
        private int whiteReservePieces;
        private int blackReservePieces;
        private int hashCode;
        private int moves;

        public Piece[,] Pieces
        {
            get
            {
                return this.board;
            }
            set
            {
            }
        }

        public Boolean WhiteToMove
        {
            get
            {
                return this.whiteToMove;
            }
            set
            {
            }
        }

        public Boolean WhiteWon
        {
            get
            {
                return this.whiteWon;
            }
            set
            {
            }
        }

        public Boolean BlackWon
        {
            get
            {
                return this.blackWon;
            }
            set
            {
            }
        }

        public Boolean Tie
        {
            get
            {
                return this.tie;
            }
            set
            {
            }
        }

        public int WhiteReservePieces
        {
            get
            {
                return this.whiteReservePieces;
            }
            set
            {
            }
        }

        public int BlackReservePieces
        {
            get
            {
                return this.blackReservePieces;
            }
            set
            {
            }
        }

        public int NumMoves
        {
            get
            {
                return this.moves;
            }
            set
            {
            }
        }

        public State()
        {
            this.board = new Piece[4, 10];
            this.rn = new RandomNumbers();
            this.whiteToMove = true;

            this.blackReservePieces = 15;
            this.whiteReservePieces = 15;

            generateHashCode();
        }

        private State(Piece[,] board, RandomNumbers rn, Boolean whiteWon, Boolean blackWon, Boolean whiteToMove, int whiteReservePieces, int blackReservePieces, int hashCode, int moves)
        {
            this.board = board;
            this.rn = rn;
            this.whiteWon = whiteWon;
            this.blackWon = blackWon;
            this.whiteToMove = whiteToMove;
            this.whiteReservePieces = whiteReservePieces;
            this.blackReservePieces = blackReservePieces;
            this.hashCode = hashCode;
            this.moves = moves;
            this.tie = false;
        }

        public void print()
        {
            String toWrite = "";
            Console.WriteLine("\n\n\n");
            Console.WriteLine((whiteToMove ? "White to Move" : "Black to Move"));
            Console.WriteLine("White Pieces: " + whiteReservePieces + "\t\t\tTotal moves: " + moves);
            Console.WriteLine("Black Pieces: " + blackReservePieces);
            Console.WriteLine("_________\t\t_______\t\t_____\t\t___");

            for (int i = 0; i < 4; i++)
            {
                toWrite = "|";

                for (int j = 0; j < 4; j++)
                {
                    if (board[i, j] != null)
                    {
                        toWrite += board[i, j].ToString() + (board[i, j].Moveable ? "T" : "F");
                        toWrite += "|";
                    }
                    else
                    {
                        toWrite += " |";
                    }
                }

                if (i < 3)
                {
                    toWrite += "\t\t|";

                    for (int j = 4; j < 7; j++)
                    {
                        if (board[i, j] != null)
                        {
                            toWrite += board[i, j].ToString() + (board[i, j].Moveable ? "T" : "F");
                            toWrite += "|";
                        }
                        else
                        {
                            toWrite += " |";
                        }


                    }
                }

                if (i < 2)
                {
                    toWrite += "\t\t|";

                    for (int j = 7; j < 9; j++)
                    {
                        if (board[i, j] != null)
                        {
                            toWrite += board[i, j].ToString() + (board[i, j].Moveable ? "T" : "F");
                            toWrite += "|";
                        }
                        else
                        {
                            toWrite += " |";
                        }
                    }
                }

                if (i == 0)
                {
                    toWrite += "\t\t|";
                    if (board[0, 9] != null)
                    {
                        toWrite += board[0, 9].ToString() + (board[0, 9].Moveable ? "T" : "F");
                        toWrite += "|";
                    }
                    else
                    {
                        toWrite += " |";
                    }
                }
                Console.WriteLine(toWrite);

                if (i == 0)
                {
                    Console.WriteLine("_________\t\t_______\t\t_____\t\t___");
                }
                else if (i < 2)
                {
                    Console.WriteLine("_________\t\t_______\t\t_____");
                }
                else if (i < 3)
                {
                    Console.WriteLine("_________\t\t_______");

                }
                else
                {
                    Console.WriteLine("_________");
                }
            }

            if (whiteWon)
            {
                Console.Out.WriteLine("White won");
            }
            else if (blackWon)
            {
                Console.Out.WriteLine("Black won");
            }
            else if (Tie)
            {
                Console.Out.WriteLine("Move limit reached. Game Tied");
            }
        }
		
        public Action[] getActions()
        {
            Action[] toReturn = new Action[30];
            int index = 0;
            for (int i = 0; i < 4; i++)
            {
                for (int j = 0; j < 4; j++)
                {
                    toReturn[index] = isLegal(j, i);
                    index++;
                }
            }
            for (int i = 4; i < 7; i++)
            {
                for (int j = 0; j < 3; j++)
                {
                    toReturn[index] = isLegal(j, i);
                    index++;
                }
            }
            for (int i = 7; i < 9; i++)
            {
                for (int j = 0; j < 2; j++)
                {
                    toReturn[index] = isLegal(j, i);
                    index++;
                }
            }
            toReturn[index] = isLegal(0, 9);
            return toReturn;
        }

       public void update(Action a) //need to update moveability as well
        {
            for (int i = 0; i < 3; i++)
            {
                if (a[i] != null)
                {
                    Point src = a[i].Source; //x = y = -1 if placing a piece from our reserve
                    Point dest = a[i].Destination; //x = y = -1 if removing a piece from the board

                    if (src.X == -1 && src.Y == -1)
                    {
                        board[dest.Y, dest.X] = new Piece(whiteToMove, dest.X, dest.Y);

                        if (whiteToMove)
                        {
                            hashCode ^= rn[1, whiteReservePieces]; //remove existing pieces
                            whiteReservePieces--;
                            hashCode ^= rn[3, dest.Y, dest.X]; //remove no pieces at destination
                            hashCode ^= rn[1, dest.Y, dest.X]; //add a piece at destination
                            hashCode ^= rn[1, whiteReservePieces]; //add new existing pieces
                        }
                        else
                        {
                            hashCode ^= rn[2, blackReservePieces];
                            blackReservePieces--;
                            hashCode ^= rn[3, dest.Y, dest.X];
                            hashCode ^= rn[2, dest.Y, dest.X];
                            hashCode ^= rn[2, blackReservePieces];
                        }

                        if (dest.X == 9 && dest.Y == 0)
                        {
                            whiteWon = whiteToMove;
                            blackWon = !whiteToMove;
                        }

                        updateMoveability(src, dest, 1);
                    }
                    else if (dest.X == -1 && dest.Y == -1)
                    {
                        board[src.Y, src.X] = null;

                        if (whiteToMove)
                        {
                            hashCode ^= rn[1, whiteReservePieces];
                            whiteReservePieces++;
                            hashCode ^= rn[1, src.Y, src.X]; //remove the piece at source
                            hashCode ^= rn[3, src.Y, src.X]; //add an empty at source
                            hashCode ^= rn[1, whiteReservePieces];
                        }
                        else
                        {
                            hashCode ^= rn[2, blackReservePieces];
                            blackReservePieces++;
                            hashCode ^= rn[2, src.Y, src.X];
                            hashCode ^= rn[3, src.Y, src.X];
                            hashCode ^= rn[2, blackReservePieces];
                        }
                        updateMoveability(src, dest, 2);
                    }
                    else
                    {
                        board[dest.Y, dest.X] = board[src.Y, src.X];
                        board[dest.Y, dest.X].Location = dest;

                        board[src.Y, src.X] = null;

                        if (whiteToMove)
                        {
                            hashCode ^= rn[1, src.Y, src.X]; //remove the piece at source
                            hashCode ^= rn[3, src.Y, src.X]; //add an empty at source

                            hashCode ^= rn[3, dest.Y, dest.X]; //remove empty at destination
                            hashCode ^= rn[1, dest.Y, dest.X]; //add a piece at destination
                        }
                        else
                        {
                            hashCode ^= rn[2, src.Y, src.X];
                            hashCode ^= rn[3, src.Y, src.X];

                            hashCode ^= rn[3, dest.Y, dest.X];
                            hashCode ^= rn[2, dest.Y, dest.X];
                        }

                        if (dest.X == 9 && dest.Y == 0)
                        {
                            whiteWon = whiteToMove;
                            blackWon = !whiteToMove;
                        }
                        updateMoveability(src, dest, 3);
                    }
                }
            }

            hashCode ^= rn.WhiteToMove;
            hashCode ^= rn.BlackToMove;

            if (whiteReservePieces == 0 && !whiteWon)
            {
                blackWon = true;
            }
            else if (blackReservePieces == 0 && !blackWon)
            {
                whiteWon = true;
            }

            this.moves++;

            if (this.moves == 250)
            {
                tie = true;
            }

            whiteToMove = !whiteToMove;
        }

        public Object clone()
        {
            Piece[,] b = new Piece[4, 10];

            for (int i = 0; i < 4; i++)
            {
                for (int j = 0; j < 10; j++)
                {
                    if (board[i, j] != null)
                    {
                        b[i, j] = (Piece)board[i, j].clone();
                    }
                    else
                    {
                        b[i, j] = null;
                    }
                }
            }

            return new State(b, rn, whiteWon, blackWon, whiteToMove, whiteReservePieces, blackReservePieces, hashCode, moves);
        }

        public override int GetHashCode()
        {
            return this.hashCode;
        }

        public override bool Equals(object obj)
        {
            if (obj == null)
            {
                return false;
            }

            State s = obj as State;

            if (s == null)
            {
                return false;
            }
            
            return s.GetHashCode() == this.GetHashCode();
        }

        private void updateMoveability(Point src, Point dest, int code) //1 means placed a piece on from reserve, 2 means removed a piece, 3 means promoted a piece to a higher or equal level
        {
            //TODO update moveabilty of pieces here
        }

        private Action isLegal(int j, int i)
        {
            Action action;
            Move[] moves = new Move[1];
            //Check if location has a piece.
            if (board[j, i] == null)
            {
                //If not follow this tree
                //Layer 1
                if (i < 4)
                {
                    moves[0] = new Move(new Point(-1, -1), new Point(j, i));
                    action = new Action(moves);
                    return action;
                }
                //Layer 2
                if (i < 7)
                {
                    if (board[j, (i - 4)] == null)
                        return null;
                    if (board[(j + 1), (i - 4)] == null)
                        return null;
                    if (board[j, (i - 3)] == null)
                        return null;
                    if (board[(j + 1), (i - 3)] == null)
                        return null;
                    moves[0] = new Move(new Point(-1, -1), new Point(j, i));
                    action = new Action(moves);
                    return action;
                }
                //Layer 3
                if (i < 9)
                {
                    if (board[j, (i - 3)] == null)
                        return null;
                    if (board[(j + 1), (i - 3)] == null)
                        return null;
                    if (board[j, (i - 2)] == null)
                        return null;
                    if (board[(j + 1), (i - 2)] == null)
                        return null;
                    moves[0] = new Move(new Point(-1, -1), new Point(j, i));
                    action = new Action(moves);
                    return action;
                }
                //Top Layer
                if (board[j, (i - 3)] == null)
                    return null;
                if (board[(j + 1), (i - 3)] == null)
                    return null;
                if (board[j, (i - 2)] == null)
                    return null;
                if (board[(j + 1), (i - 2)] == null)
                    return null;
                moves[0] = new Move(new Point(-1, -1), new Point(j, i));
                action = new Action(moves);
                return action;
            }
            else
            {
                //If there is a piece there
                if (board[j, i].Moveable)
                {
                    moves[0] = new Move(new Point(j, i), new Point(-1, -1));
                    action = new Action(moves);
                    return action;
                }
                else
                    return null;
            }
        }

       private void generateHashCode()
        {
            hashCode = 0;
            for (int i = 0; i < 9; i++)
            {
                for (int j = 0; j < ((i < 4) ? 4 : ((i < 7) ? 3 : 2)); j++)
                {
                    hashCode ^= rn[3, j, i];
                }
            }

            hashCode ^= rn[3, 0, 9];
            hashCode ^= rn[1, 15];
            hashCode ^= rn[2, 15];
            hashCode ^= rn.WhiteToMove;
        }
    }
}
